using System.Text.Json;
using System.Reflection;
using MultiAgentsClient.Service.WorkflowEngine;
using MultiAgentsClient.Service.WorkflowEngine.BuiltInNodes;
using MultiAgentsShared;
using MultiAgentsClient.Service.WorkflowEngine.BaseNodes;

public static class WorkflowConfigJsonExtension
{
    private static BindingFlags JsonBindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
    private static JsonSerializerOptions options
    {
        get
        {
            return new JsonSerializerOptions
            {
                Converters =
                {
                    new ListVariableConverter<string>()
                }
            };
        }
    }


    public static void FromJson(this BehaviorTreeConfig self, string json)
    {
        var jsonDoc = JsonDocument.Parse(json);
        var root = jsonDoc.RootElement;

        // Deserialize TriggerNode
        if (root.TryGetProperty("TriggerNode", out var triggerNodeElement))
        {
            self.TriggerNode = DeserializeNode(triggerNodeElement);
        }

        // Deserialize OperationNodes
        if (root.TryGetProperty("OperationNodes", out var operationNodesElement) && operationNodesElement.ValueKind == JsonValueKind.Array)
        {
            self.OperationNodes = [];
            foreach (var nodeElement in operationNodesElement.EnumerateArray())
            {
                self.OperationNodes.Add(DeserializeNode(nodeElement));
            }
        }

        // Deserialize Connections
        if (root.TryGetProperty("Connections", out var connectionsElement) && connectionsElement.ValueKind == JsonValueKind.Array)
        {
            self.connections = new List<WorkflowConnection>();
            foreach (var connectionElement in connectionsElement.EnumerateArray())
            {
                self.connections.Add(DeserializeConnection(connectionElement));
            }
        }
    }

    private static BaseNode? DeserializeNode(JsonElement element)
    {
        try
        {
            if (element.TryGetProperty("Type", out var typeElement))
            {
                var type = Type.GetType(typeElement.GetString());
                if (type != null)
                {
                    var node = (BaseNode)Activator.CreateInstance(type);
                    foreach (var prop in element.EnumerateObject())
                    {
                        var propertyName = prop.Name;
                        if (propertyName == "Type") continue;  // Skip type property, it's just for instantiation

                        // Attempt to deserialize fields if no property with that name was found
                        var propertyInfo = type.GetProperty(propertyName, JsonBindingFlags);
                        var fieldInfo = type.GetField(propertyName, JsonBindingFlags);
                        if (propertyInfo != null)
                        {
                            var value = JsonSerializer.Deserialize(prop.Value.GetRawText(), propertyInfo.PropertyType, options);
                            propertyInfo.SetValue(node, value);
                        }
                        else if (fieldInfo != null)
                        {
                            var value = JsonSerializer.Deserialize(prop.Value.GetRawText(), fieldInfo.FieldType, options);
                            fieldInfo.SetValue(node, value);
                        }
                    }
                    return node;
                }
            }

            return null;
        }
        catch
        {
            Debug.LogError("Failed to deserialize");
            return null;
        }
    }

    private static WorkflowConnection DeserializeConnection(JsonElement element)
    {
        var connection = new WorkflowConnection();
        var connectionType = connection.GetType();
        foreach (var prop in element.EnumerateObject())
        {
            var propertyInfo = connectionType.GetProperty(prop.Name, JsonBindingFlags);
            if (propertyInfo != null)
            {
                var value = JsonSerializer.Deserialize(prop.Value.GetRawText(), propertyInfo.PropertyType);
                propertyInfo.SetValue(connection, value);
            }
            else
            {
                // Attempt to deserialize fields if no property with that name was found
                var fieldInfo = connectionType.GetField(prop.Name, JsonBindingFlags);
                if (fieldInfo != null)
                {
                    var value = JsonSerializer.Deserialize(prop.Value.GetRawText(), fieldInfo.FieldType);
                    fieldInfo.SetValue(connection, value);
                }
            }
        }
        return connection;
    }
}
